package org.little.beans.factory.support;

import org.little.beans.BeanDefinition;
import org.little.beans.BeansException;
import org.little.beans.factory.BeanCreationException;
import org.little.beans.factory.FactoryBean;
import org.little.beans.factory.config.RuntimeBeanReference;
import org.little.beans.factory.config.TypedStringValue;

/**
 * @author Tinyspot
 * @date 2022-05-21 16:29
 */
public class BeanDefinitionValueResolver {

    private final AbstractBeanFactory beanFactory;

    public BeanDefinitionValueResolver(AbstractBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    /**
     * 属性解析
     * RuntimeBeanReference 解析 ref="accountDao"
     * TypedStringValue 解析 value="xiao"
     * @param value
     * @return
     */
    public Object resolveValueIfNecessary(Object value) {
        if (value instanceof RuntimeBeanReference) {
            RuntimeBeanReference reference = (RuntimeBeanReference) value;
            String beanName = reference.getBeanName();
            Object bean = this.beanFactory.getBean(beanName);
            return bean;
        } else if (value instanceof TypedStringValue) {
            return ((TypedStringValue) value).getValue();
        } else if (value instanceof BeanDefinition) {
            BeanDefinition beanDefinition = (BeanDefinition) value;
            String innerBeanName = "(inner bean)" + beanDefinition.getBeanClassName() + "#"
                    + Integer.toHexString(System.identityHashCode(beanDefinition));
            return resolveInnerBean(innerBeanName, beanDefinition);
        } else {
            // throw new RuntimeException("this value " + value + " has not implemented");
            return value;
        }
    }

    private Object resolveInnerBean(String innerBeanName, BeanDefinition beanDefinition) {
        try {
            Object innerBean = this.beanFactory.createBean(beanDefinition);
            if (innerBean instanceof FactoryBean) {
                try {
                    return ((FactoryBean<?>) innerBean).getObject();
                } catch (Exception e) {
                    throw new BeanCreationException(innerBeanName, "FactoryBean threw exception on Object creation", e);
                }
            } else {
                return innerBean;
            }
        } catch (BeansException e) {
            throw new BeanCreationException(innerBeanName, "Cannot create inner bean '" + innerBeanName + "'"
                    + (beanDefinition != null && beanDefinition.getBeanClassName() != null ? "of type [" + beanDefinition.getBeanClassName() + "]" : "")
                    , e);
        }
    }
}
